#!coding:utf-8
'''
Created on Dec 17, 2012
所有易宝支付相关代码
@author: join
'''
import socket
import binascii
from ISO8583 import ISO8583
from myPyDes import MyDes
#from pyqt.main import conf
import sys
from ConfigParser import ConfigParser
import os
import datetime
import urllib

reload(sys)
sys.setdefaultencoding("utf-8")
if sys.platform=='win32':
    from ctypes import windll, create_string_buffer
import time
import threading
from PyQt4 import QtCore

DLL_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),"KMY350X.dll")
if not os.path.exists(DLL_PATH):
    DLL_PATH='c:\windows\KMY350X.dll'

if sys.platform=='win32':    
    CONF_PATH=os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),"yeepaydata.conf")
    if not os.path.exists(CONF_PATH):
        CONF_PATH="G:\lottery_terminal\pyqt\yeepaydata.conf"
else:
    CONF_PATH=os.path.join(os.path.dirname(os.path.dirname(__file__)),'yeepaydata.conf')

    
DECRYPT = 1
ENCRYPT = 0

class ErrorTracer(object):
    """错误跟踪并上报"""
    def __init__(self):
        self.url = 'http://117.41.181.89:8000'
        
    def reportError(self,errormsg):
        urllib.urlopen('%s/%s/%s'%(self.url,'reporterror',errormsg))
    
class YeepayClient(object):
    '''易宝支付组包接口合辑，包括：下载主密钥，签到，签退，结算，储值卡消费，储值卡撤单，储值卡冲正'''
    _SERVER_IP = '' #conf['yeepay_server_ip']
    _SERVER_PORT = 28000 # conf['yeepay_server_port']
    _SERVER_PORT2 = 29000 #TMS socket端口号码
    
    _YEEPAY_TERMINAL_NUMBER = '' #易宝终端号
    _YEEPAY_SERIAL_NUMBER = '' #易宝终端序列号
    _YEEPAY_CUSTOMER_NUMBER = '' #易宝商户号
    _YEEPAY_ROLLBACK_TIMES = ''
    _YEEPAY_SOFTWARE_VERSION = ''
    _YEEPAY_PARAMS_VERSION = ''
    _YEEPAY_TPDU = ''

    _SERIAL_NUMBER = 0 #11域签到初始流水号,
    _BATCH_NUMBER = 0 #60域签到初始批次号
    
    _MASTER_KEY = '1111111111111111' #主密钥
    _TRANSPORT_KEY = ''
    _MAC_KEY = '' #mac密钥
    _PIN_KEY = '' #pin密钥
    _PIN_BLOCK = '' #pin运算结果
    
    _CONSUME_INFO = [] #交易信息，用于结算，冲正
    _ROLLBACK_DATA=None
    
    def __init__(self,debug=False):
        '''易宝支付初始化'''
        YeepayClient._SERIAL_NUMBER = Utils().read_config("pos_serial_number")
        YeepayClient._BATCH_NUMBER = Utils().read_config("pos_batch_number")
        self.work_key = None
        self.debug = debug
        self.server_ip = YeepayClient._SERVER_IP
        self.server_port = int(YeepayClient._SERVER_PORT)
        self.server_port2 = int(YeepayClient._SERVER_PORT2)
        self.rollbacktime = 0
    
    def signin(self):
        '''签到'''
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.connect((self.server_ip,self.server_port))
        yutil = Utils()
        batch_number = int(yutil.read_config("pos_batch_number"))
        yutil.update_config("pos_batch_number", str(batch_number+1).zfill(6))
        serial_number = yutil.consume_serial_number()
        #iso8583数据报文封装
        iso8583 = ISO8583(debug=False)
        iso8583.setTransationType('0800')
        #生成流水号
        iso8583.setBit(11, serial_number)
        
        iso8583.setBit(41, YeepayClient._YEEPAY_TERMINAL_NUMBER) #易宝终端号
        iso8583.setBit(42, YeepayClient._YEEPAY_CUSTOMER_NUMBER) #易宝商户号
#        value57 = '00'+'01'+' '*18+'11'+' '*18
        value57 = '00'+YeepayClient._YEEPAY_SOFTWARE_VERSION+' '*(20-len(YeepayClient._YEEPAY_SOFTWARE_VERSION)) + YeepayClient._YEEPAY_PARAMS_VERSION+' '*(20-len(YeepayClient._YEEPAY_PARAMS_VERSION))
        iso8583.setBit(57, value57)
        
        batch_number = str(batch_number).zfill(6)
        value60 = '%s%s%s'%('00',batch_number, '001') #签到类码，批次号，管理信息码
        iso8583.setBit(60, value60)
        
        value63 = '%s'%('001')
        iso8583.setBit(63, value63)

        try:
            message = '%s' % iso8583.getNetworkISO()
            sock.send(binascii.unhexlify(message))
            ans = sock.recv(2048)
            response=binascii.b2a_hex(ans)
            #解析返回报文
            isoAns = ISO8583(iso=response,debug=False)
            self.work_key = isoAns.getBit(62)
            self.parse_work_key()
            Utils().update_config("pos_serial_number", YeepayClient._SERIAL_NUMBER) #更新流水号
            return True
        except Exception as e:
            errorTracer = ErrorTracer()
            errorTracer.reportError('signin exception is:%s'%e)
            return False
        finally:
            sock.close()
    
    def signout(self):
        '''签退'''
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.connect((self.server_ip,self.server_port))
#        sock.connect(('172.16.10.5',8583))
        
        #iso8583数据报文封装
        iso8583 = ISO8583(debug=False)
        iso8583.setTransationType('0820')
        
        serial_number = Utils().consume_serial_number()
        iso8583.setBit(11, serial_number)
#        iso8583.setBit(11, '000001')
        iso8583.setBit(41, YeepayClient._YEEPAY_TERMINAL_NUMBER)
        iso8583.setBit(42, YeepayClient._YEEPAY_CUSTOMER_NUMBER)
#        value57 = '00'+'01'+' '*18+'11'+' '*18
        value57 = '00'+YeepayClient._YEEPAY_SOFTWARE_VERSION+' '*(20-len(YeepayClient._YEEPAY_SOFTWARE_VERSION)) + YeepayClient._YEEPAY_PARAMS_VERSION+' '*(20-len(YeepayClient._YEEPAY_PARAMS_VERSION))
        iso8583.setBit(57, value57)
        batch_number = Utils().read_config("pos_batch_number")
        batch_number = str(batch_number).zfill(6)
        value60 = '%s%s%s'%('00',batch_number, '002')
        iso8583.setBit(60, value60)

        try:
            message = '%s' % iso8583.getNetworkISO()
            sock.send(binascii.unhexlify(message))
            ans = sock.recv(2048)
            response=binascii.b2a_hex(ans)
            #解析返回报文
            isoAns = ISO8583(iso=response,debug=False)
            msg_type = isoAns.getMTI()
            if msg_type == '0830':
                return True
            else:
                return False
        except Exception as e:
            errorTracer = ErrorTracer()
            errorTracer.reportError('signout exception is:%s'%e)
            return False
        finally:
            sock.close()
    
    def download_posp_params(self):
        client = YeepayClient()
        mainkey = client.pos_request()
            
        mainkey_chinese = mainkey.decode('hex').decode('gb2312')
        try:
            with file('yeepayparams','w') as f:
                f.write(mainkey_chinese)
        except Exception as e:
            print 'exception is:',e
        finally:
            f.close()
            
    def pos_request(self):
        '''请求报文'''
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.connect((self.server_ip,self.server_port2))
        
        datagram = ''
        TPDU = '6000040000'
        datagram += TPDU
        
        data = ''
        order_code = '10' #命令码
        data = '%s%s'%(data,binascii.b2a_hex(order_code))
        pos_cati = YeepayClient._YEEPAY_TERMINAL_NUMBER #pos终端号
        data = '%s%s'%(data,binascii.b2a_hex(pos_cati))
        soft_version = 'V2011111801'+' '*9
        data = '%s%s'%(data,binascii.b2a_hex(soft_version))
#        param_version = ' '*20
#        data = '%s%s'%(data,binascii.b2a_hex(param_version))
        serial_number = YeepayClient._YEEPAY_SERIAL_NUMBER+' '*(30-len(YeepayClient._YEEPAY_SERIAL_NUMBER)) #pos序列号
        data = '%s%s'%(data,binascii.b2a_hex(serial_number))
        imes_card_number = '1'+' '*29 #IMES卡号
        data = '%s%s'%(data,binascii.b2a_hex(imes_card_number))
        lrc = self.gen_lrc(data) #待计算
        data = '%s%s'%(data,lrc)
        data_length = hex(len(data)/2)[2:].zfill(4) #数据长度
        data = '%s%s'%(data_length,data)
        
        datagram = '%s%s'%(datagram,data)
        datagram_length = hex(len(datagram)/2)[2:].zfill(4) #数据报文长度
        datagram = '%s%s'%(datagram_length,datagram)
        
        sock.send(binascii.a2b_hex(datagram))
        response = sock.recv(1024)
        response_a = binascii.b2a_hex(response)
        soft_version = response_a[38:78] #软件版本号
        
        param_taskid = response_a[94:110] #参数任务id
        param_version = response_a[110:150] #参数版本号
        param_length = response_a[150:166] #参数长度
        length = int(binascii.unhexlify(param_length))
        start = 0
        params = ''
        while length>0:
            if length/1000>0:
                download_length = 1000
            else:
                download_length = length%1000
            download_start = ('%s%s'%('',start)).zfill(8)
            start += download_length
            length -= download_length
            download_length = ('%s%s'%('',download_length)).zfill(4)
            result = self.download(param_taskid,param_version,download_length,download_start)
            params += result[30:len(result)-2]
            
        self.download_announce(param_taskid,param_version,serial_number)
        return params
    
    def download(self,taskid,version,download_length,start):
        '''下载主密钥，下载报文'''
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.connect((self.server_ip,self.server_port2))
        
        datagram = ''
        TPDU = '6000040000'
        datagram += TPDU
        
        data = ''
        order_code = '11' #命令码
        data = '%s%s'%(data,binascii.b2a_hex(order_code))
        pos_cati = YeepayClient._YEEPAY_TERMINAL_NUMBER #pos终端号
        data = '%s%s'%(data,binascii.b2a_hex(pos_cati))
#        taskid = '00000001' #任务id
#        data = '%s%s'%(data,binascii.b2a_hex(taskid))
        data = '%s%s'%(data,taskid)
        old_version = 'V2011111801'+' '*9
        data = '%s%s'%(data,binascii.b2a_hex(old_version))
#        new_version = 'V2011010501'+' '*9
#        data = '%s%s'%(data,binascii.b2a_hex(new_version))
        data = '%s%s'%(data, version)
        transtype = '1' #下载类型，0表示软件，1表示参数
        data = '%s%s'%(data,binascii.b2a_hex(transtype))
        data = '%s%s'%(data,binascii.b2a_hex(start))
#        length = '1000' #可下载的最大长度
#        data = '%s%s'%(data,binascii.b2a_hex(length))
        data = '%s%s'%(data,binascii.b2a_hex(download_length))
        serial_number = YeepayClient._YEEPAY_SERIAL_NUMBER+' '*(30-len(YeepayClient._YEEPAY_SERIAL_NUMBER)) #pos序列号
        data = '%s%s'%(data,binascii.b2a_hex(serial_number))
        lrc = self.gen_lrc(data) #待计算
        data = '%s%s'%(data,lrc)
        data_length = hex(len(data)/2)[2:].zfill(4) #数据长度
        data = '%s%s'%(data_length,data)
        
        datagram = '%s%s'%(datagram,data)
        datagram_length = hex(len(datagram)/2)[2:].zfill(4) #数据报文长度
        datagram = '%s%s'%(datagram_length,datagram)
        
#        print '+++++++++++++datagram is:++++++++++++',datagram
        sock.send(binascii.a2b_hex(datagram))
        response = sock.recv(1152)
        return binascii.b2a_hex(response)
        
    def download_announce(self,taskid=None,version=None,serial_number=None):
        '''下载通知接口'''
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.connect((self.server_ip,self.server_port2))
        
        datagram = ''
        TPDU = '6000040000'
        datagram += TPDU
        
        data = ''
        order_code = '12' #命令码
        data = '%s%s'%(data,binascii.b2a_hex(order_code))
        pos_cati = YeepayClient._YEEPAY_TERMINAL_NUMBER #pos终端号
        data = '%s%s'%(data,binascii.b2a_hex(pos_cati))
        taskid = binascii.b2a_hex('00000001') if not taskid else taskid #任务id
        data = '%s%s'%(data,taskid)
        version = binascii.b2a_hex('V2011111801'+' '*9) if not version else version
        data = '%s%s'%(data,version)
        data = '%s%s'%(data,binascii.b2a_hex(serial_number))
        lrc = self.gen_lrc(data) #待计算
        data = '%s%s'%(data,lrc)
        data_length = hex(len(data)/2)[2:].zfill(4) #数据长度
        data = '%s%s'%(data_length,data)
        
        datagram = '%s%s'%(datagram,data)
        datagram_length = hex(len(datagram)/2)[2:].zfill(4) #数据报文长度
        datagram = '%s%s'%(datagram_length,datagram)
        
        sock.send(binascii.a2b_hex(datagram))
    
    def consume(self,money,card_number):
        '''储值卡消费'''
        card_client = CardClient(card_number)
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.connect((self.server_ip,self.server_port))
        
        #iso8583消费数据报文封装
        iso8583 = ISO8583(debug=False)
        iso8583.setTransationType('0200')
        iso8583.setBit(3,'000000')
        
        value4 = ('%s%s'%(money,'00')).zfill(12)
        iso8583.setBit(4,value4) #交易金额
        
#        YeepayClient._SIGNIN_SERIAL_NUMBER = YeepayClient._SIGNIN_SERIAL_NUMBER+1  #TODO:
        serial_number = Utils().consume_serial_number()
        Utils().update_config("pos_serial_number", serial_number)
        iso8583.setBit(11,serial_number) #流水号
        iso8583.setBit(22,'021') #磁条pin可输入：021
        iso8583.setBit(25,'00') #正常提交：00，预授权：06
        iso8583.setBit(26,'06') #个人密码明文最大长度，06表示6位
        
        value35 = card_client.getCardSecondNumber() #6222601310010029726d4912120106702687
        iso8583.setBit(35,value35) #第二磁道数据
        value36 = card_client.getCardThirdNumber() #996222601310010029726d1561560500050008168013000000010000000000ddd0702687106
        if value36:
            iso8583.setBit(36,value36) #第三磁道数据
        iso8583.setBit(41, YeepayClient._YEEPAY_TERMINAL_NUMBER) #终端号
        iso8583.setBit(42, YeepayClient._YEEPAY_CUSTOMER_NUMBER) #商户号
        iso8583.setBit(49, '156') #人民币
        
#        #输入密码，读取密码密文
#        keyboard = KeyboardClient(value35)
#        pin_block = keyboard.read_pin_block()
        times=0
        while not YeepayClient._PIN_BLOCK and times<3:
            times += 1
            time.sleep(1)
        iso8583.setBit(52, YeepayClient._PIN_BLOCK) #8个字节个人密码密文
        YeepayClient._PIN_BLOCK = '' #reset
        iso8583.setBit(53, '2000000000000000') #与安全相关的控制信息,加密方法+加密标识+0，2：Ansi x9.8带主帐号，0：des算法
        
        batch_number = Utils().read_config("pos_batch_number")
        value60 = '%s%s'%('22',batch_number) #22:消费/冲正
        iso8583.setBit(60, value60) #类型码+批次号
#        mac2='CDF876CF'
        iso8583.setMacBit()
        data = iso8583.getRawIso()[22:]
        mac=Utils().genMac(data, YeepayClient._MASTER_KEY, YeepayClient._MAC_KEY)
#        mac = self.gen_mac(data, YeepayClient._MASTER_KEY, YeepayClient._MAC_KEY)
        iso8583.setMac(binascii.b2a_hex(mac)) #消息校验码
        try:
            message = '%s' % iso8583.getNetworkISO()
            sock.send(binascii.unhexlify(message))
            ans = sock.recv(2048)
            response=binascii.b2a_hex(ans)
            
            isoAns = ISO8583(iso=response,debug=False)
            value_39 = isoAns.getBit(39)
            if value_39 == '00':
                #持久化消费信息
#                now = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
#                data = "%s##%s##%s##%s##%s"%(value4,serial_number,value35[:19],mac,now)
                detail={'money':value4,'serial_number':serial_number,'card_number':value35[:19],'status':'success','mac':mac,'responsecode':value_39}
                YeepayClient._CONSUME_INFO.append(detail)
                YeepayClient._ROLLBACK_DATA = {'money':value4,'serial_number':serial_number,'mac':True,'errorcode':'96','value60':value60}
            elif value_39=='68' or value_39=='98':
                #超时，发起冲正请求,响应错误码统一填 98
                self.rollback_consume(value4,serial_number,'98',value60)
            elif value_39=='63':
                #mac校验出错
                self.rollback_consume(value4,serial_number,'A0',value60)
            elif value_39=='94': #批次号+流水号重复
                self.rollback_consume(value4,serial_number,'06',value60)
            elif value_39=='10': #批次号+流水号重复
                self.rollback_consume(value4,serial_number,'06',value60,True)
            else:
                pass
        
            return value_39
#            v1=isoAns.getBitsAndValues()
#            for v in v1:
#                print ('Bit %s of type %s with value = %s' % (v['bit'],v['type'],v['value']))
        except Exception as e:
            errorTracer = ErrorTracer()
            errorTracer.reportError('consume reponse datagram:%s'%e)
            return 'exception'
        finally:
            sock.close()
    
#    def persist_consume(self,datastr): TODO:结算
#        '''持久化消费信息'''
#        path = os.path.join(os.path.dirname(DLL_PATH),'consumedata')
    
    def rollback_consume(self,money,pay_number,errorcode,value60,mac=False):
        '''储值卡消费冲正,当消费时返回00或10时需要生成mac值'''
        self.rollbacktime += 1
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.connect((self.server_ip,self.server_port))
        iso8583 = ISO8583(debug=False)
        iso8583.setTransationType('0400')
        iso8583.setBit(3,'000000') #交易处理码，同原交易
        iso8583.setBit(4,money) #交易金额，同原交易请求消息
        iso8583.setBit(11,pay_number) #原交易流水号
        iso8583.setBit(22, '021') #同原交易
        iso8583.setBit(25,'00') #同原交易
        iso8583.setBit(39, errorcode) #冲正原因
        
        iso8583.setBit(41, YeepayClient._YEEPAY_TERMINAL_NUMBER)
        iso8583.setBit(42, YeepayClient._YEEPAY_CUSTOMER_NUMBER)
        iso8583.setBit(49, '156')
        iso8583.setBit(60, value60)
        #如果需要mac值
        if mac:
            iso8583.setMacBit()
            data = iso8583.getRawIso()[22:]
            mac=Utils().genMac(data, YeepayClient._MASTER_KEY, YeepayClient._MAC_KEY)
            iso8583.setMac(binascii.b2a_hex(mac)) #消息校验码
        
        try:
            message = '%s' % iso8583.getNetworkISO()
            sock.send(binascii.unhexlify(message))
            ans = sock.recv(2048)
            response=binascii.b2a_hex(ans)
            isoAns = ISO8583(iso=response,debug=False)
            value_39 = isoAns.getBit(39)
            if value_39 not in ['00','25','12'] and self.rollbacktime < 3: #继续发起充正
                self.rollback_consume(money, pay_number, errorcode, value60, mac)
            else:
                self.rollbacktime = 0 #冲正成功，复位
        except Exception as e:
            errorTracer = ErrorTracer()
            errorTracer.reportError('rollback consume exception is:%s'%e)
        finally:
            sock.close()
    
#    def report_transactions(self,transactions):
#        '''批上送,目前用不上'''
#        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#        sock.connect((self.server_ip,self.server_port))
#        iso8583 = ISO8583(debug=False)
#        iso8583.setTransationType('0300')
#        iso8583.setBit(11,str(YeepayClient._SIGNIN_SERIAL_NUMBER+1)) #原交易流水号
#        
#        iso8583.setBit(41, YeepayClient._YEEPAY_TERMINAL_NUMBER)
#        iso8583.setBit(42, YeepayClient._YEEPAY_CUSTOMER_NUMBER)
#        trans_detials = '' #磁卡交易明细
#        trans_detials += str(len(transactions))
#        for tran in transactions:
#            tran_detail = '00'+tran['serial_number']+tran['card_number'].zfill(20)+ tran['money'] #类别+交易流水号+卡号+交易金额
#            trans_detials += tran_detail
#        iso8583.setBit(48, trans_detials)
#        iso8583.setBit(60, '00'+str(YeepayClient._SIGNIN_BATCH_NUMBER))
#
#        try:
#            message = '%s' % iso8583.getNetworkISO()
#            print 'Sending ... %s' % message
#            sock.send(binascii.unhexlify(message))
#            ans = sock.recv(2048)
#            print '++++++++++++++++++++++++'
#            response=binascii.b2a_hex(ans)
#            print 'roll_back receive info is:',response
#        except Exception as e:
#            print 'exception is:',e
#        finally:
#            sock.close()
    
    def undo_consume(self):
        '''储值卡消费撤单'''
        pass
    
    def parse_work_key(self):
        '''将签到的工作密钥解析成pin密钥和mac密钥'''
        self.work_key_ascii = binascii.b2a_hex(self.work_key).upper() #签到后获取的工作密钥
        YeepayClient._PIN_KEY = self.work_key_ascii[:16]
        CAL_PIN_CHKSUM_HEX = self.gen_chksum(YeepayClient._PIN_KEY)
        if self.debug:
            print '+++++generated pin check sum is:',CAL_PIN_CHKSUM_HEX
        PIN_CHKSUM_HEX = self.work_key_ascii[16:24].upper()
        if self.debug:
            print '+++++response check sum is:',PIN_CHKSUM_HEX
        if CAL_PIN_CHKSUM_HEX != PIN_CHKSUM_HEX:
            raise Exception('pin checksum is not consistent.')
        else:
            if self.debug:
                print 'pin ok.pin is:',YeepayClient._PIN_KEY
        
        YeepayClient._MAC_KEY = self.work_key_ascii[24:40]
        CAL_MAC_CHKSUM_HEX = self.gen_chksum(YeepayClient._MAC_KEY)
        MAC_CHKSUM_HEX = self.work_key_ascii[40:48].upper()
        if CAL_MAC_CHKSUM_HEX != MAC_CHKSUM_HEX:
            raise Exception('mac checksum is not consistent.')
        else:
            if self.debug:
                print 'mac ok.',YeepayClient._MAC_KEY
            
    def balance(self,total_money,total_transaction):
        '''批次结算，结算后交易金额将会转入注册商银联账户'''
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.connect((self.server_ip,self.server_port))
        
        iso8583 = ISO8583(debug=False)
        iso8583.setTransationType('0500')
        #生成流水号
        serial_number = Utils().consume_serial_number()
        iso8583.setBit(11, serial_number)
        
        iso8583.setBit(41, YeepayClient._YEEPAY_TERMINAL_NUMBER)
        iso8583.setBit(42, YeepayClient._YEEPAY_CUSTOMER_NUMBER)
        #48域值
        totalmoney_1 = str(total_money).replace('.', '').zfill(12) #借记总金额，即消费总额
        totalnumber_1 = str(total_transaction).zfill(3) #借记总笔数
        out_total_money = '00'.zfill(12) #贷记总金额
        out_total_number = '000'
        response_code = '0' #对帐应答码
        other_money = '0'*31 #外卡结算
        value48 = "%s%s%s%s%s%s"%(totalmoney_1,totalnumber_1,out_total_money,out_total_number,response_code,other_money)
        iso8583.setBit(48, value48)
        
        iso8583.setBit(49, '156')
        batch_number = Utils().read_config("pos_batch_number")
        value60 = '%s%s%s'%('00',batch_number, '001')
        iso8583.setBit(60, value60)
        iso8583.setBit(63, '001')
        
        try:
            message = '%s' % iso8583.getNetworkISO()
            sock.send(binascii.unhexlify(message))
            ans = sock.recv(2048)
            response=binascii.b2a_hex(ans)
            
            isoAns = ISO8583(iso=response)
            money_posp = isoAns.getBit(48)  #posp返回对帐信息
            if money_posp[-1] == '1':
                return True
            else:
#                self.sponsor_report_trans()
                return False
        except Exception as e:
            print 'exception is:',e
            return False
        finally:
            sock.close()
            
    #当前不需要，不一致，直接联系易宝客服
#    def sponsor_report_trans(self): 
#        '''发起批上送请求,当发生对帐不一致时，执行披上送。。'''
#        i = 0
#        while True:
#            print 'start sponsor transactions report.'
#            to_report = YeepayClient._CONSUME_INFO[i*8:(i+1)*8]
#            if not to_report:
#                break
#            self.report_transactions(to_report)
#            i += 1
    
    def gen_lrc(self,source):
        '''生成一个字节的校验位'''
        MAB = []
        #初始化MAB数组
        leng = len(source)/2
        for i in range(leng):
            MAB.append(source[i*2:(i+1)*2])
        
        #对MAB块依次异或
        m0_bit = self.string_to_BitList(binascii.unhexlify(MAB[0]))
        j=1
        while j<len(MAB):
            m1_bit = self.string_to_BitList(binascii.unhexlify(MAB[j]))
            m0_bit = list(map(lambda x,y:x^y, m0_bit, m1_bit))
            j += 1
            
        lrc = '%s%s'%(self.binary2Hex(m0_bit[:4]),self.binary2Hex(m0_bit[-4:]))
        return lrc
        
    def gen_chksum(self,source):
        '''生成pin密钥和mac密钥的checksum'''
        myDes = MyDes()
        cal_key = myDes.des(source, YeepayClient._MASTER_KEY, DECRYPT)
        cal_source = '0000000000000000'
        chk_sum = myDes.des(cal_source, cal_key, ENCRYPT)
        return chk_sum[:8]
    
    def gen_mac(self,data,master_key,mac_key):
        '''根据报文消息，mac密钥，主密钥，生成mac校验码'''
        self.api = windll.LoadLibrary(DLL_PATH)
        self.api.OpenPort(KeyboardClient._COM_PORT,9600)
        iret=self.api.LoadMainKey(0,master_key,'')
        if iret != 0:
            raise Exception("load main key error:%s"%iret)
                
        self.auth_code = create_string_buffer('\000'*64)
        iret=self.api.LoadWorkKey(0,1,mac_key,self.auth_code)
        if iret != 0:
            raise Exception("load work key error,%s"%iret)
        
        iret=self.api.ActivateWorkKey(0,1)
        if iret != 0:
            raise Exception('activate work key error,%s'%iret)

        self.uc_data = create_string_buffer('\000'*256)
        self.api.SetMACArithmetic(3)
        self.api.MACAdd(data,self.uc_data,3)

        return self.uc_data.value[:8]
    
    def binary2Hex(self,data):
        '''将二进制字符数组转换成16进制字符串'''
        data_len = len(data)
        if data_len>4:
            return None
        
        ord_value = 0
        for i in range(data_len):
            if data[i] == 0:
                ord_value += 0
            else:
                ord_value += (data[i]*2)**(data_len-i-1)
        return hex(ord_value)[2:]
    
    def string_to_BitList(self, data):
        """将数据转换成二进制数组"""
        data = [ord(c) for c in data] #转换成十进制数
        l = len(data) * 8
        result = [0] * l
        pos = 0
        for ch in data:
            i = 7
            while i >= 0:
                if ch & (1 << i) != 0:
                    result[pos] = 1
                else:
                    result[pos] = 0
                pos += 1
                i -= 1

        return result
    
    def parse_yeepay_params(self):
        '''解析易宝参数文件'''
        try:
            myDes = MyDes()
            params_handler = open('yeepayparams')
            lines = params_handler.readlines()
            for line in lines:
                if line.startswith('终端主密钥密文'):
                    master_key = line.split('=')[1]
                    #decrypt master key
                    YeepayClient._MASTER_KEY = myDes.des(master_key, YeepayClient._TRANSPORT_KEY, DECRYPT)
                elif line.startswith(''):
                    pass
        except Exception as e:
            print 'exception is:',e
        finally:
            params_handler.close()

class KeyboardClient(QtCore.QObject):
    '''键盘客户端，获取键盘输入'''
    _PIN_BLOCK=''
    _COM_PORT=''
    
    def __init__(self,account):
        super(KeyboardClient,self).__init__()
        self.api = windll.LoadLibrary(DLL_PATH)
        account = str(account)
        dindex = account.find('=') #取第二磁道|第三磁道数据=左边第二位开始的12位
        self.account = account[dindex-13:dindex-1]
#            account = account[1:20].replace('=','D')
            
        self.OpenPort()
        
    def OpenPort(self):
        self.api.OpenPort(KeyboardClient._COM_PORT,9600)
        
    def OpenKeyboard(self):
        self.api.KeyboardControl(2)  #打开密码键盘
        
    def closeKeyboard(self):
        self.api.KeyboardControl(1) #关闭密码键盘
        
    def closePort(self):
        self.api.ClosePort()
    
    def pin_settings(self):
        '''pin密钥密文生成的相关设置'''
        self.uckey=''
        self.auth_code = create_string_buffer('\000'*64)

        iret=self.api.LoadMainKey(0,YeepayClient._MASTER_KEY,'') #TODO:此处需要替换主密钥
        if iret != 0:
            raise Exception("load main key error:%s"%iret)
        
        self.auth_code = create_string_buffer('\000'*64)
        iret=self.api.LoadWorkKey(0,0,YeepayClient._PIN_KEY,self.auth_code) 
        if iret != 0:
            raise Exception("load work key error,%s"%iret)
        
        iret=self.api.ActivateWorkKey(0,0)
        if iret != 0:
            raise Exception('activate work key error,%s'%iret)
        
        iret=self.api.SetAccount(str(self.account)) #131001002972,倒数第二位向前取12个数字即是下载帐号
        if iret!=0:
            raise Exception("set account error,%s"%iret)

        iret=self.api.SetEcbCbcMode(0)#设置加密模式为ECB
        if iret!=0:
            raise Exception("SetEcbCbcMode error,%s"%iret)

        iret=self.api.SetEncryptMethod(1)#设置加密算法为DES
        if iret!=0:
            raise Exception("SetEncryptMethod error,%s"%iret)

        iret=self.api.SetPINArithmetic(1)#设置pin加密算法
        if iret!=0:
            raise Exception("SetPINArithmetic error,%s"%iret)
    
    def read_pin_block(self):
        '''pin密码密文读取'''
        self.pin_settings()
        self.api.KeyboardControl(2)
        #启动键盘输入
        pin_thread = threading.Thread(target=self.read_keyboard)
        pin_thread.start()
        
        return True

    def read_keyboard(self):
        '''读取密码键盘密码明文'''
        iret=self.api.PinStart(6,1,60)
        iScoutKeypressPIN = 1
        pin_input_len = 0
        if iret != 0:
            raise Exception("pinstart error:%s"%iret)

        while iScoutKeypressPIN == 1:
            self.point_variable = create_string_buffer('\000'*9)
            iret=self.api.ScoutKeyPress(self.point_variable)
            if self.point_variable.value:
                if self.point_variable[1]=='D': #确认：OX0D；取消：0x1B；更正：0x08
                    iScoutKeypressPIN = 0
                    self.emit(QtCore.SIGNAL("complete_password()"))
                    break
                elif self.point_variable.value == '02': #密码输入超时
                    iScoutKeypressPIN = 0
                    self.emit(QtCore.SIGNAL("update_password(int)"),0)
                    break
                elif self.point_variable.value == '08': #更正按钮，密码向前一步
                    self.emit(QtCore.SIGNAL("update_password(int)"),2)
                    pin_input_len = 0
                elif self.point_variable.value == '1B': #取消按钮，pin计算结束,退出刷卡流程。
                    self.emit(QtCore.SIGNAL("update_password(int)"),3)
                    break
                else:
                    pin_input_len += 1
                    self.emit(QtCore.SIGNAL("update_password(int)"),1)
            time.sleep(0.06)
            
        self.uc_data = create_string_buffer('\000'*120)
        self.api.KeyboardControl(1)
        iret=self.api.ReadPinBlock(self.uc_data)
        if iret == 0:
            self.closePort()
            YeepayClient._PIN_BLOCK = self.uc_data.value
            return True
        else:
            self.closePort()
            return False

class CardClient(object):
    '''银联卡客户端，负责与银联磁条卡相关的交互，后续会增加IC卡的相关功能'''
    def __init__(self,card_number):
        self.card_number = str(card_number)
        
    #获取磁条卡第二磁道数据
    def getCardSecondNumber(self):
        if self.card_number.startswith(';'):
            sec_number = self.card_number.split('?')[0]
            sec_number = sec_number[1:].replace('=','D')
            return sec_number
        else:
            print 'the card not exist data 2'
            raise Exception('the card not exist data 2')
    
    #获取磁条卡第三磁道数据
    def getCardThirdNumber(self):
        index = self.card_number.find('+')
        if index==-1:
            print 'no third card number'
            return ''
        third_number = self.card_number[index+1:-1].replace('=','D')
        return third_number
     
class Utils(object):
    '''工具类'''
    def consume_serial_number(self):
        serial_number = self.read_config("pos_serial_number")
        serial_number = int(serial_number)
        if serial_number == 100:#到达300笔换一个批次号，从新计算,TODO:自动发起批结算
            self.update_config("pos_serial_number", '100000')
            batch_number = int(self.read_config("pos_batch_number"))
            self.update_config("pos_batch_number", str(batch_number+1).zfill(6))
        else:
            self.update_config("pos_serial_number", str(serial_number+1))
        serial_number = str(serial_number+1).zfill(6)
        return serial_number
    
    def update_config(self,name,value,section='default'):
        c = ConfigParser()
        try:
            c.read(CONF_PATH)
            c.set(section, name, value)
            c.write(open(CONF_PATH, "w"))
        except Exception as e:
            print "can not update config : yeepaydata.conf %s"%e
            
    def read_config(self,name,section='default'):
        c = ConfigParser()
        try:
            c.read(CONF_PATH)
            config = dict(c.items(section))
            return config[name]
        except Exception as e:
            print "can not read config : yeepaydata.conf %s"%e
            return None
            
    def genMac(self,data,master_key,mac_key):
        #DO XOR
        des=MyDes()
    #    mac_key = des.des(mac_key, master_key, 1)#解密mac key
        mac_key = des.des(mac_key, master_key, 1)#解密mac key
        
        tempRes=data[:16]
        m0_bit=self.get_hex_bit(tempRes)
        data=data[16:]
        while len(data)>0:
            if(len(data)<16):
                mab=data[:16]+'0'*(16-len(data))
            else:
                mab=data[:16]
            m1_bit=self.get_hex_bit(mab)
            data=data[16:]
            m0_bit=list(map(lambda x,y:x^y, m0_bit, m1_bit))
    
        tempXor = self.binary2Hex(m0_bit)
        temp2 = binascii.b2a_hex(tempXor)
        temp21=temp2[:16]
        temp22=temp2[16:]
        #用mac对temp21加密
        des = MyDes()
        decryptRes = des.des(temp21, mac_key, 0) #E94CBF2769A0131B
        #加密后结果与temp22异或
        decryptRes_bit = self.get_hex_bit(decryptRes)
        temp22_bit = self.get_hex_bit(temp22)
        xor_bit=list(map(lambda x,y:x^y, decryptRes_bit, temp22_bit))
        xor_bit_hex=self.binary2Hex(xor_bit)
        #异或后的结果再进行des加密
        cryptRes = des.des(xor_bit_hex, mac_key, 0)
        return cryptRes[:8]
    
    def get_hex_bit(self,data):
        data = [int(c,16) for c in data] #转换成十进制数
        l = len(data) * 4
        result = [0] * l
        pos = 0
        for ch in data:
            i = 3
            while i >= 0:
                if ch & (1 << i) != 0:
                    result[pos] = 1
                else:
                    result[pos] = 0
                pos += 1
                i -= 1
        
        return result
    
    def binary2Hex(self,source):
        '''将二进制字符数组转换成16进制字符串'''
        result = ''
    #    if data_len>4:
    #        return None
        while len(source)>1:
            data=source[:4]
            source=source[4:]
            ord_value = 0
            for i in range(4):
                if data[i] == 0:
                    ord_value += 0
                else:
                    ord_value += (data[i]*2)**(4-i-1)
            result += hex(ord_value)[2:]
        return result.upper()

if __name__ == '__main__':
#    client.download_posp_params()
    YeepayClient._SERVER_IP='119.161.147.124'
    YeepayClient._YEEPAY_TERMINAL_NUMBER = '60101008'
    YeepayClient._YEEPAY_CUSTOMER_NUMBER = '874110145112480'
    YeepayClient._SERVER_PORT = '28000'
    YeepayClient._TRANSPORT_KEY = '68756972616E6E63'
#    client = YeepayClient()
#    client.signin()
#    client.download_announce()
#    client.signout()
#    client.download()

#    data='0200302004C030C0981100000000000001200000000402100006366222601310010029726D49121201067026870076996222601310010029726D1561560500050008168013000000010000000000DDD070268710603630313031303038383734313130313435313132343830313536D90A49DD1386C8522000000000000000000822000004'
#    mackey='97709A058FE2EB18'
#    masterkey='1111111111111111'
#    client.gen_mac(data,masterkey,mackey)
    util = Utils()
    
